package com.jm.cloud.feign.result;

import com.jm.framework.common.exception.BaseResultCodeEnum;
import com.jm.framework.common.exception.BizException;
import com.jm.framework.common.result.BaseResult;
import feign.FeignException;
import feign.Response;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.io.IOException;
import java.lang.reflect.Type;

/**
 * tide
 * <p>
 * Feign结果解码器
 * <p>
 * author: jiangjianmin
 * <p>
 * create: 2024-08-22 15:20
 **/
@Slf4j
public class FeignResultDecoder implements Decoder {
    private final SpringDecoder decoder;

    public FeignResultDecoder(SpringDecoder decoder) {
        this.decoder = decoder;
    }

    /**
     * 解码Feign调用返回的数据
     * 此方法用于处理API响应，仅当响应表示成功且基结果代码匹配时，才返回解包后的实际结果数据
     * 否则，抛出业务异常，异常将包含错误代码和消息
     *
     * @param response 响应对象，包含API调用的返回数据
     * @param type     目标类型，预期解码后的数据类型
     * @return 解码后的数据，类型由参数type指定
     * @throws IOException     当读取响应数据时发生I/O错误
     * @throws DecodeException 当解码响应数据时发生错误
     * @throws FeignException  当响应指示HTTP级别的错误
     */
    @Override
    public Object decode(Response response, Type type) throws IOException, FeignException {
        // 通用逻辑
        ParameterizedTypeImpl resultWrapperType = ParameterizedTypeImpl.make(BaseResult.class, new Type[]{type}, null);
        BaseResult<?> resultWrapper;
        try {
            resultWrapper = (BaseResult<?>) this.decoder.decode(response, resultWrapperType);
        } catch (Exception e) {
            return this.decoder.decode(response, type);
        }
        if (!BaseResultCodeEnum.SUCCESS.getCode().equals(resultWrapper.getCode())) {
            // 此时抛异常会被捕获成DecoderException，会通过FeignExceptionHandler中的decoderException()处理再重新抛出
            throw new BizException(resultWrapper.getCode(), resultWrapper.getMessage());
        }
        log.info("feign 响应结果 {}", resultWrapper);
        return resultWrapper.getData();
    }
}
